package cn.tedu.charging.device.dao.repository.impl;

import cn.tedu.charging.common.constant.CacheKeyConst;
import cn.tedu.charging.common.pojo.query.NearStationsQuery;
import cn.tedu.charging.device.dao.mapper.GunMapper;
import cn.tedu.charging.device.dao.mapper.StationMapper;
import cn.tedu.charging.device.dao.repository.DeviceRepository;
import cn.tedu.charging.device.pojo.po.ChargingGunInfoPO;
import cn.tedu.charging.device.pojo.po.ChargingStationPO;
import cn.tedu.charging.device.pojo.po.StationCanalPO;
import cn.tedu.charging.device.pojo.po.StationGeoPO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.GeoOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Repository;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Repository
@Slf4j
public class DeviceRepositoryImpl implements DeviceRepository {
    @Autowired
    private StationMapper stationMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private GunMapper gunMapper;

    @Override
    public List<ChargingStationPO> getStations() {
        //select * from charging_stations
        return stationMapper.selectList(null);
    }

    @Override
    public void saveGeos(List<ChargingStationPO> pos) {
        GeoOperations geoOps = redisTemplate.opsForGeo();
        //geoadd charging:stations:all lng1 lat1 1 lng2 lat2 2 lng3 lat3 3
        //geoadd charging:stations:all lng2 lat2 2
        //geoadd charging:stations:all lng3 lat3 3
        /*for (ChargingStationPO po : pos) {
            geoOps.add(
                    CacheKeyConst.GEO_STATIONS,
                    new Point(po.getStationLng().doubleValue(),po.getStationLat().doubleValue()),
                    po.getId());
        }*/
        //geoadd charging:stations:all lng1 lat1 1 lng2 lat2 2 lng3 lat3 3
        List<RedisGeoCommands.GeoLocation> geoLocations=null;
        geoLocations=pos.stream().map(po->{
            //将数据库持久化po映射封装成GeoLocation
            RedisGeoCommands.GeoLocation geoLocation=
                    new RedisGeoCommands.GeoLocation(
                            po.getId(),
                            new Point(po.getStationLng().doubleValue(),
                                    po.getStationLat().doubleValue()));
            return geoLocation;
        }).collect(Collectors.toList());
        //执行写入redis
        redisTemplate.opsForGeo().add(CacheKeyConst.GEO_STATIONS,geoLocations);
    }

    @Override
    public Boolean geoStationsExists() {
        //exists charging:stations:all
        return redisTemplate.hasKey(CacheKeyConst.GEO_STATIONS);
    }

    @Override
    public Long countAll() {
        //select count(0) from charging_stations
        return stationMapper.selectCount(null);
    }

    @Override
    public List<ChargingStationPO> getStationsPage(Long start, Long rows) {
        //select * from charging_station limit start,rows
        QueryWrapper<ChargingStationPO> queryWrapper=new QueryWrapper<>();
        queryWrapper.last("limit "+start+","+rows);
        return stationMapper.selectList(queryWrapper);
    }

    @Override
    public List<StationGeoPO> nearStations(NearStationsQuery query) {
        GeoOperations<String,Integer> geoOps = redisTemplate.opsForGeo();
        //1.查询geo集合 georadius key lng lat radius [km] withcoord withdist
        Circle circle=new Circle(
                new Point(query.getLongitude(),query.getLatitude()),query.getRadius());
        RedisGeoCommands.GeoRadiusCommandArgs args=
                RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
                        .includeCoordinates()
                        .includeDistance();
        GeoResults<RedisGeoCommands.GeoLocation<Integer>> geoResults = geoOps.radius(CacheKeyConst.GEO_STATIONS, circle, args);
        //1.1geoResult获取2个值 avgDistance List<GeoResult>
        List<GeoResult<RedisGeoCommands.GeoLocation<Integer>>> results = geoResults.getContent();
        //2.使用results做流api映射 每一个GeoResult 封装成PO
        List<StationGeoPO> pos=null;
        if (results!=null&&results.size()>0) {
            pos=results.stream().map(result->{
                //2.1result id distance lng lat
                Distance distance = result.getDistance();//16.762km
                BigDecimal distValue=new BigDecimal(distance.getValue()+"").setScale(2, RoundingMode.HALF_UP);
                Integer stationId=result.getContent().getName();
                double lng = result.getContent().getPoint().getX();
                double lat = result.getContent().getPoint().getY();
                //2.2stationName stationStatus 使用stationId 读取statinPO
                ChargingStationPO stationPO = getStationById(stationId);
                String stationName=stationPO.getStationName();
                Integer stationStatus=stationPO.getStationStatus();
                StationGeoPO po=new StationGeoPO();
                po.setStationId(stationId);
                po.setStationName(stationName);
                po.setStationStatus(stationStatus);
                po.setStationLng(lng);
                po.setStationLat(lat);
                po.setDistance(distValue);
                return po;
            }).collect(Collectors.toList());
        }
        return pos;
    }

    @Override
    public String getStationName(Integer stationId) {
        return "";
    }

    @Override
    public ChargingStationPO getStationById(Integer stationId) {
        //拿到一个 redis操作string数据结构的客户端对象 设置key value泛型
        ValueOperations<String,ChargingStationPO> valueOps = redisTemplate.opsForValue();
        //操作缓存读写使用key
        String stationCacheKey=CacheKeyConst.STATION_DETAIL_PREFIX+stationId;
        //1.读取缓存 get key
        ChargingStationPO stationPO = valueOps.get(stationCacheKey);
        //判断没命中
        if(stationPO==null){
            log.debug("场站缓存数据未命中,场站id:{}",stationId);
            //3.读取数据库
            stationPO = stationMapper.selectById(stationId);
            //4.保证下次读取可以命中缓存 需要使用它恢复缓存数据 回填
            valueOps.set(stationCacheKey,stationPO);
        }else{
            log.debug("场站缓存数据命中,场站id:{}",stationId);
        }
        return stationPO;
    }

    @Override
    public List<ChargingGunInfoPO> getStationGuns(Integer stationId) {
        //使用stationId查询 gun
        return gunMapper.selectByStationId(stationId);
    }

    @Override
    public Boolean updateGunStatus(Integer gunId, Integer status) {
        return null;
    }

    @Override
    public void saveStation(StationCanalPO stationCanalPO) {

    }

    @Override
    public void updateStation(StationCanalPO before, StationCanalPO after) {

    }

    @Override
    public void deleteStation(StationCanalPO stationCanalPO) {

    }

    @Override
    public Long countGunByIdAndStatus(Integer gunId, Integer status) {
        return 0l;
    }


}
